/**
 * DIY智能鱼缸控制软件轻量版 FishAiLite
 * 小鱼智能 www.fishai.cn
 * 
 * 版本: beta 1
 *
 * ** 需具备一定的动手能力，自备硬件，注意安全用电 **
 *
 * 关于源码：
 * 1. 本源码采用VSCode + PIO插件开发，也可以采用Arduino IDE编译。
 *    依赖库: blinker, NTPClient, U8g2, OneWire, DallasTemperature
 * 2. 开源软件，请自觉保留作者的信息
 * 3. 更多信息欢迎查看作者的B站视频: 准备中...
 *
 * 自备硬件:
 *  1. ESP32开发板或者ESP8266开发板 1个
 *  2. 继电器 ESP32支持10个
 *  3. 温度传感器(DS18B20) 1个
 *  4. 水位检测浮球 1个
 *  5. 0.96英寸OLED 4脚显示屏 1个
 *  6. 电源，5V降压模块
 *  7. 连接线材，如杜邦线，电线等
 *  8. 其他被控设备：如灯，水泵灯
 *
 * ESP32开发板引脚分配和点灯App对应关系, 见define.h
 *
 * 功能:
 * 1. 水温检测显示
 * 2. 自动补水：通过浮球检测水位，低水位时控制一路继电器。此继电器接入补水泵
 * 3. App远程控制: 通过免费的 "点灯APP" 远程控制继电器，实现开关灯等功能
 *
 * 配置:
 * 1. 下载"点灯APP"
 * 2. 新建一个设备，获得密钥
 * 3. 将密钥和家里的Wifi密码（只支持2.4G）输入到本代码中，编译后上传到ESP32中即可。
 *
 * 高级功能需定制：
 * 1. 显示你的名字或自定义的文字
 * 2. 通过连接ESP32的热点，可在网页上自由更改密钥和Wifi密码
 * 3. 定时开关灯功能
 * 4. 控制制冷片正负极转换，实现冷热切换自动恒温
 * 5. 连接高清IPS显示屏，显示更加丰富的信息
 */

#include <Arduino.h>

// 请修改Wifi账号和密码，以及点灯科技App的设备密钥
const char *wifi_ssid = "wifi账号";
const char *wifi_pswd = "wifi密码";
const char *blinker_auth = "点灯科技App的设备密钥";

// 要先引入Blinker库
#define BLINKER_PRINT Serial
#define BLINKER_WIFI
#include <Blinker.h>

// 网络对时
#include <NTPClient.h>
// NTP对时
WiFiUDP ntpUDP;
NTPClient ntpClient(ntpUDP, "ntp5.aliyun.com", 60 * 60 * 8, 300000);

//温度传感器
#include <DallasTemperature.h> 
OneWire owTemp;
DallasTemperature waterTemp;

// 本程序的模块
#include "setup.h"
#include "process.h"

// 绑定Blinker的组件
BlinkerNumber blkTemp("temp"); //温度值
BlinkerText blkWater("water");
BlinkerButton k1("k1"); //开关1 对应引脚见 define.h
BlinkerButton k2("k2"); //开关2
BlinkerButton k3("k3"); //开关3
BlinkerButton k4("k4"); //开关4
BlinkerButton k5("k5"); //开关5
BlinkerButton k6("k6"); //开关6
BlinkerButton k7("k7"); //开关7
BlinkerButton k8("k8"); //开关8
BlinkerButton k9("k9"); //开关9
BlinkerButton k10("k10"); //开关10

// 响应Blinker的组件事件
void k1Callback(const String & state);
void k2Callback(const String & state);
void k3Callback(const String & state);
void k4Callback(const String & state);
void k5Callback(const String & state);
void k6Callback(const String & state);
void k7Callback(const String & state);
void k8Callback(const String & state);
void k9Callback(const String & state);
void k10Callback(const String & state);
void blkNotBind(const String & data); //如果未绑定的组件被触发，则会执行其中内容

void blkDataStorage(); //存储历史数据
void blkRtData(); //实时更新温度数据
void blkHeartBeat(); //心跳

// 启动后的入口
void setup()
{
    // 串口调试
    Serial.begin(115200);
    Serial.println("setup...");

    // 开始设置
    owTemp.begin(TEMP_PIN);
    waterTemp.setOneWire(&owTemp); //DS18B20温度传感器
    waterTemp.begin();
    Setup::begin();

    // 连接并检查网络
    Blinker.begin(blinker_auth, wifi_ssid, wifi_pswd);
    Setup::chkWifi(&WiFi, wifi_ssid, wifi_pswd);
    Serial.println("setup Wifi End");

    // 启动网络对时
    ntpClient.begin();

    // 绑定组件
    Blinker.attachHeartbeat(blkHeartBeat);
    Blinker.attachDataStorage(blkDataStorage); //历史数据
    Blinker.attachRTData(blkRtData); //实时数据
    Blinker.attachData(blkNotBind); 
    k1.attach(k1Callback); // 开关事件
    k2.attach(k2Callback);
    k3.attach(k3Callback);
    k4.attach(k4Callback);
    k5.attach(k5Callback);
    k6.attach(k6Callback);
    k7.attach(k7Callback);
    k8.attach(k8Callback);
    k9.attach(k9Callback);
    k10.attach(k10Callback);

    blkHeartBeat();
    // 设置结束
    Serial.println("setup end");
}

// 循环调用的函数
void loop()
{
    // 网络对时
    ntpClient.update();

    // Blinker更新
    Blinker.run();

    // 处理数据
    if( Process::run(ntpClient.getFormattedTime()) )
    {
        blkWater.print( Input::getWaterLevel() == WATER_LOW ? "水位低!" : "正常" );
    }
}

// 如果未绑定的Blinker组件被触发，则会执行其中内容
void blkNotBind(const String & data)
{
    BLINKER_LOG("Blinker readString: ", data);
    Serial.println(data);
}

// 心跳
void blkHeartBeat()
{
    k1.print(Process::getRelayState(K1_PIN));
    k2.print(Process::getRelayState(K2_PIN));
    k3.print(Process::getRelayState(K3_PIN));
    k4.print(Process::getRelayState(K4_PIN));
    k5.print(Process::getRelayState(K5_PIN));
    k6.print(Process::getRelayState(K6_PIN));
    k7.print(Process::getRelayState(K7_PIN));
    k8.print(Process::getRelayState(K8_PIN));
    k9.print(Process::getRelayState(K9_PIN));
    k10.print(Process::getRelayState(K10_PIN));
}

// 存储历史数据
void blkDataStorage()
{
    TEMP_VALUE temp = Input::getTemp();
    if( temp.valid )
    {
        Blinker.dataStorage("tempHis", temp.value);
    }
    Serial.println("tblkDataStorageempHis");
}

// 实时更新温度数据
void blkRtData()
{
    TEMP_VALUE temp = Input::getTemp();
    Blinker.sendRtData("temp", temp.value);

    Serial.println("blkRtData");
}

// 本地和App上日志调试
void echoBlkLog(String name, String state) 
{
    String log = String("Blinker ") +name +": "+ state;
    BLINKER_LOG(log);
    Serial.println(log);
}

// 开关1 事件处理
void k1Callback(const String & state)
{
    echoBlkLog("k1", state);
    k1.print(Process::relayAct(K1_PIN, state));
}

// 开关2 事件处理
void k2Callback(const String & state)
{
    echoBlkLog("k2", state);
    k2.print(Process::relayAct(K2_PIN, state));
}

// 开关3 事件处理
void k3Callback(const String & state)
{
    echoBlkLog("k3", state);
    k3.print(Process::relayAct(K3_PIN, state));
}

// 开关4 事件处理
void k4Callback(const String & state)
{
    echoBlkLog("k4", state);
    k4.print(Process::relayAct(K4_PIN, state));
}

// 开关5 事件处理
void k5Callback(const String & state)
{
    echoBlkLog("k5", state);
    k5.print(Process::relayAct(K5_PIN, state));
}

// 开关6 事件处理
void k6Callback(const String & state)
{
    echoBlkLog("k6", state);
    k6.print(Process::relayAct(K6_PIN, state));
}

// 开关7 事件处理
void k7Callback(const String & state)
{
    echoBlkLog("k7", state);
    k7.print(Process::relayAct(K7_PIN, state));
}

// 开关8 事件处理
void k8Callback(const String & state)
{
    echoBlkLog("k8", state);
    k8.print(Process::relayAct(K8_PIN, state));
}

// 开关9 事件处理
void k9Callback(const String & state)
{
    echoBlkLog("k9", state);
    k9.print(Process::relayAct(K9_PIN, state));
}

// 开关10 事件处理
void k10Callback(const String & state)
{
    echoBlkLog("k10", state);
    k10.print(Process::relayAct(K10_PIN, state));
}
